package com.game.framwork.gamesupport;

import java.util.LinkedList;

import android.graphics.Rect;
import android.util.Log;

import com.game.framwork.itf.Game;
import com.game.framwork.itf.Graphics;
import com.game.object.JObject;
import com.game.tiledmap.TiledMap;

public class QuadTree {

	private int width;
	private int height;
	private int maxLevel;
	private QuadTreeNode pRootNode;
	//private Game game;
	public TiledMap tiledMap;

	public QuadTree(int maxLevel, int width, int height, Game game) {
		//this.game = game;
		this.maxLevel = maxLevel;
		this.width = width;
		this.height = height;
		pRootNode = new QuadTreeNode(null, 0, 0, width, height);
	}

	public QuadTreeNode subDivide(QuadTreeNode pNode) {
		if (pNode.nodeLevel < this.maxLevel) {
			if (pNode.pSubNode == null) {
				int nodeLeftX = pNode.upperLeftX;
				int nodeLeftY = pNode.upperLeftY;
				int nodeWidth = pNode.width / 2;
				int nodeHeight = pNode.height / 2;

				pNode.pSubNode = new QuadTreeNode[4];

				pNode.pSubNode[0] = new QuadTreeNode(pNode, nodeLeftX,
						nodeLeftY, nodeWidth, nodeHeight);
				pNode.pSubNode[1] = new QuadTreeNode(pNode, nodeLeftX
						+ nodeWidth, nodeLeftY, nodeWidth, nodeHeight);
				pNode.pSubNode[2] = new QuadTreeNode(pNode, nodeLeftX,
						nodeLeftY + nodeHeight, nodeWidth, nodeHeight);
				pNode.pSubNode[3] = new QuadTreeNode(pNode, nodeLeftX
						+ nodeWidth, nodeLeftY + nodeHeight, nodeWidth,
						nodeHeight);

				return pNode;
			} else
				return pNode;
		}

		return pNode;
	}

	public void addObject(JObject pOjbect) {
		addObject(pOjbect, null);
	}

	private void addObject(JObject pObject, QuadTreeNode pNode) {
		if (pNode == null) {
			pNode = this.pRootNode;
		}

		int collisionIndex = 0;
		int[] index = { -1, -1 };
		Rect[] virtualNodeRect = new Rect[4];
		int nodeLeftX = pNode.upperLeftX;
		int nodeLeftY = pNode.upperLeftY;
		int nodeWidth = pNode.width / 2;
		int nodeHeight = pNode.height / 2;

		virtualNodeRect[0] = new Rect((int) nodeLeftX, (int) nodeLeftY,
				(int) nodeLeftX + (int) nodeWidth, (int) nodeLeftY
						+ (int) nodeHeight);
		virtualNodeRect[1] = new Rect((int) (nodeLeftX + nodeWidth),
				(int) nodeLeftY, (int) (nodeLeftX + nodeWidth)
						+ (int) nodeWidth, (int) nodeLeftY + (int) nodeHeight);
		virtualNodeRect[2] = new Rect((int) nodeLeftX,
				(int) (nodeLeftY + nodeHeight), (int) nodeLeftX
						+ (int) nodeWidth, (int) (nodeLeftY + nodeHeight)
						+ (int) nodeHeight);
		virtualNodeRect[3] = new Rect((int) (nodeLeftX + nodeWidth),
				(int) (nodeLeftY + nodeHeight), (int) (nodeLeftX + nodeWidth)
						+ (int) nodeWidth, (int) (nodeLeftY + nodeHeight)
						+ (int) nodeHeight);

		for (int i = 0; i < 4; i++) {
			if (pObject.getBoundBox().intersect(virtualNodeRect[i])) {
				index[collisionIndex++] = i;
				if (collisionIndex > 1)
					break;
			}
		}

		if (collisionIndex == 0) {
			return;
		}
		if (collisionIndex > 1) {
			pNode.addObject(pObject);
		} else {
			QuadTreeNode temptNode = pNode;
			pNode = subDivide(temptNode);
			if (pNode.pSubNode != null) {
				addObject(pObject, pNode.pSubNode[index[0]]);
			} else {
				pNode.addObject(pObject);
			}
		}
	}

	public LinkedList<JObject> getObject(Rect recBound,
			LinkedList<JObject> pList) {
		LinkedList<JObject> temptList = pList;
		temptList = getObject(recBound, pList, null);
		return temptList;
	}

	private LinkedList<JObject> getObject(Rect recBound,
			LinkedList<JObject> pList, QuadTreeNode pNode) {
		LinkedList<JObject> temtList = pList;
		if (pNode == null) {
			pNode = this.pRootNode;
		}
		if (pNode.pSubNode == null) {
			LinkedList<JObject> temptList1 = temtList;
			temtList = pNode.getObject(temptList1);
			return temtList;
		} else {
			LinkedList<JObject> temptList1 = temtList;
			temtList = pNode.getObject(temptList1);
			int[] index = { -1, -1, -1, -1 };
			int collisionIndex = 0;

			for (int i = 0; i < 4; i++) {
				if (recBound.intersect(pNode.pSubNode[i].getSize())) {
					index[collisionIndex++] = i;
				}
			}
			for (int i = 0; i < collisionIndex; i++) {
				LinkedList<JObject> temptList2 = temtList;
				temtList = getObject(recBound, temptList2,
						pNode.pSubNode[index[i]]);
			}
			return temtList;
		}
	}

	public void removeObject(JObject pObject) {
		JObject removeObject = pObject;
		removeObject(removeObject, null);
	}

	private void removeObject(JObject pObject, QuadTreeNode pNode) {
		if (pNode == null) {
			pNode = this.pRootNode;
		}
		if (pNode.removeObject(pObject)) {
			return;
		}
		if (pNode.pSubNode == null) {
			return;
		} else {
			int[] index = { -1, -1, -1, -1 };
			int collisionIndex = 0;
			for (int i = 0; i < 4; i++) {
				if (pObject.getBoundBox().intersect(
						pNode.pSubNode[(int) i].getSize())) {
					index[collisionIndex++] = i;
				}
			}
			for (int i = 0; i < collisionIndex; i++) {
				removeObject(pObject, pNode.pSubNode[index[i]]);
			}
		}
	}
	
	public void attachMap(TiledMap tiledMap) {
		this.tiledMap = tiledMap;
	}

	// TODO: render
	public void update(float deltaTime) {
		LinkedList<JObject> listUpdate = new LinkedList<JObject>();
		LinkedList<JObject> listCollision = new LinkedList<JObject>();
		//Rect cameraSize = new Rect(0, 0, 800, 480);
		Rect mapSize = new Rect(0, 0, width, height);

		int sizeUpdate = 0, sizeCollision = 0;
		listUpdate = getObject(mapSize, listUpdate);
		sizeUpdate = listUpdate.size();
		Log.i("debug", "list listUpdate: " + sizeUpdate);
		for (int i = 0; i < sizeUpdate; i++) {
			if (!listUpdate.get(i).isDeath) {
				listUpdate.get(i).updateInternal(deltaTime);
			}
			if (listUpdate.get(i).isDeath) {
				removeObject(listUpdate.get(i));
			}

			listCollision.clear();
			listCollision = getObject(mapSize, listCollision);
			sizeCollision = listCollision.size();
			for (int j = 0; j < sizeCollision; j++) {
				JObject pObject = listCollision.get(j);
				listUpdate.get(i).Collision(pObject);
			}
				
			// JObject pMainObj = listUpdate.get(i);
			// MapItem.MapItemManager.getInstance().collision(pMainObj);
		}
	}

	public void paint(Graphics g) {
		LinkedList<JObject> listRender = new LinkedList<JObject>();

		int size = 0;
		Rect mapSize = new Rect(0, 0, width, height);
		listRender = getObject(mapSize, listRender);
		size = listRender.size();
			
		for (int i = 0; i < size; i++) {
			//listRender.get(i).attachTiledMap(tiledMap);
			listRender.get(i).paintInternal(g);
		}
		// MapItem.MapItemManager.getInstance().draw();

	}
}
